#define _WIN32_WINNT 0x0605
#include <windows.h>
#include <tchar.h>
#include "media_session.h"
#include <windowsx.h>
#include <shlobj.h>
#ifdef _MSC_VER
#define snwprintf _snwprintf
#endif

static HINSTANCE hinstance_;

static media_session_t session_a_;
static media_session_t session_v_;
static audio_input_t audio_in_;
static audio_output_t audio_out_;

static BOOL fullscreen_;
static BOOL mute_;

static HWND combo_video_;
static HWND combo_audio_;
static HWND check_fullscreen_;
static HWND check_mute_;

static int video_sel_;
static int audio_sel_;
static int cur_index_;
static char video_device_[256];
static char audio_device_[256];

static VOID CenterWindow(HWND hwnd){
    HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
    MONITORINFO oMonitor;
    oMonitor.cbSize = sizeof(oMonitor);
    if(!GetMonitorInfo(hMon, &oMonitor))return;

    RECT rcScreen = oMonitor.rcWork;
    RECT rcHwnd;
    if(!GetWindowRect(hwnd,&rcHwnd))return;

    int nHwndWidth = rcHwnd.right - rcHwnd.left;
    int nHwndHeight = rcHwnd.bottom - rcHwnd.top;

    int xLeft = (rcScreen.left + rcScreen.right) / 2 - nHwndWidth / 2;
    int yTop = (rcScreen.top + rcScreen.bottom) / 2 - nHwndHeight / 2;

    if(xLeft < rcScreen.left){
        xLeft = rcScreen.left;
    }else if(xLeft + nHwndWidth > rcScreen.right){
        xLeft = rcScreen.right - nHwndWidth;
    }
    if(yTop < rcScreen.top){
        yTop = rcScreen.top;
    }else if(yTop + nHwndHeight > rcScreen.bottom){
        yTop = rcScreen.bottom - nHwndHeight;
    }
    SetWindowPos(hwnd, NULL, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}


static VOID FullScreen(HWND hwnd, BOOL full){
    static RECT pos;
    static LONG style;
    static LONG styleex;
    if(full){
        HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        MONITORINFO oMonitor;        
        oMonitor.cbSize = sizeof(oMonitor);
        if(!GetMonitorInfo(hMon, &oMonitor))return;

        RECT rc = oMonitor.rcMonitor;    
        style = GetWindowLong(hwnd, GWL_STYLE);
        styleex = GetWindowLong(hwnd, GWL_EXSTYLE);
        GetWindowRect(hwnd, &pos);
        SetWindowLong(hwnd, GWL_STYLE, 0);
        SetWindowLong(hwnd, GWL_EXSTYLE, 0);
        SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW);
    } else {
        SetWindowLong(hwnd, GWL_STYLE, style);
        SetWindowLong(hwnd, GWL_EXSTYLE, styleex);        
        SetWindowPos(hwnd, HWND_NOTOPMOST, pos.left, pos.top, pos.right - pos.left, pos.bottom - pos.top, SWP_SHOWWINDOW);
    }
}


static BOOL GetSpecialFolderPath(int type, WCHAR* pszDesktopPath){
    LPITEMIDLIST  ppidl = NULL;
    if (SHGetSpecialFolderLocation(NULL, type, &ppidl) == S_OK){
        BOOL flag = SHGetPathFromIDList(ppidl, pszDesktopPath);
        CoTaskMemFree(ppidl);
        return flag;
    }
    return FALSE;
} 

static BOOL CreateShortcut(LPCTSTR lpszlnkFileName, LPCTSTR lpszExeFile, LPCTSTR lpszArg, LPCTSTR lpszWorkDir, LPCTSTR lpszDescription){
    HRESULT hr;
    BOOL ret = FALSE;
    IShellLink* pLink = NULL; 
    IPersistFile* ppf = NULL;
    CoInitializeEx(NULL,0);
    hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (void**)&pLink); if(FAILED(hr))goto clean;
    hr = pLink->lpVtbl->QueryInterface(pLink, &IID_IPersistFile, (void**)&ppf); if(FAILED(hr))goto clean;      
    pLink->lpVtbl->SetPath(pLink, lpszExeFile);
    if(lpszWorkDir)pLink->lpVtbl->SetWorkingDirectory(pLink, lpszWorkDir);
    if(lpszArg)pLink->lpVtbl->SetArguments(pLink, lpszArg);  
    if(lpszDescription)pLink->lpVtbl->SetDescription(pLink, lpszDescription); 
    hr = ppf->lpVtbl->Save(ppf, lpszlnkFileName, TRUE); if(FAILED(hr))goto clean; 
    ret = TRUE;  
clean:
    if(pLink)pLink->lpVtbl->Release(pLink);
    if(ppf)ppf->lpVtbl->Release(ppf);
    return ret;
}

static void on_device_list_item(const char* name, void* udata){
    HWND combo = udata;
    WCHAR wname[256];
    MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, 256);
    ComboBox_AddString(combo, wname);
    if(strstr(name, "CAPTURE") || strstr(name, "apture")){
        if(combo == combo_video_){
            video_sel_ = cur_index_;
        }else{
            audio_sel_ = cur_index_;
        }
    }
    cur_index_ ++;
}

static int InitDialog(HWND hwnd){
    HWND btn_ok = GetDlgItem(hwnd, 1);
    HWND btn_shortcut = GetDlgItem(hwnd, 3001);
    combo_video_ = GetDlgItem(hwnd, 1001);
    combo_audio_ = GetDlgItem(hwnd, 2001);
    check_fullscreen_ = GetDlgItem(hwnd, 1002);
    check_mute_ = GetDlgItem(hwnd, 2002);
    if(!combo_video_)return -1;
    if(!combo_audio_)return -1;
    if(!check_fullscreen_)return -1;
    cur_index_ = 0;
    video_input_device_list(on_device_list_item, combo_video_);
    cur_index_ = 0;
    audio_input_device_list(on_device_list_item, combo_audio_);
    ComboBox_SetCurSel(combo_video_, video_sel_);
    ComboBox_SetCurSel(combo_audio_, audio_sel_);
    CenterWindow(hwnd);    
    LANGID id = GetUserDefaultUILanguage();
    switch (id & 0xffff){
    case 0x0804:
    case 0x0404:
        SetWindowText(btn_ok, TEXT("开始播放"));
        SetWindowText(btn_shortcut, TEXT("创建桌面快捷方式"));
        SetWindowText(check_fullscreen_, TEXT("全屏 (按F11键切换)"));
        SetWindowText(check_mute_, TEXT("静音 (按F9键切换)"));
        SetWindowText(GetDlgItem(hwnd, 1000), TEXT("视频"));
        SetWindowText(GetDlgItem(hwnd, 2000), TEXT("音频"));
    default:
        break;
    }
    return 0;
}

static void GetConfig(){
    WCHAR wname[256];
    video_sel_ = ComboBox_GetCurSel(combo_video_);
    wname[0] = 0;
    ComboBox_GetText(combo_video_, wname, 256);
    video_device_[0] = 0;
    WideCharToMultiByte(CP_UTF8,0,wname,-1,video_device_,256,NULL,NULL);    
    audio_sel_ = ComboBox_GetCurSel(combo_audio_);
    wname[0] = 0;
    ComboBox_GetText(combo_audio_, wname, 256);
    audio_device_[0] = 0;
    WideCharToMultiByte(CP_UTF8,0,wname,-1,audio_device_,256,NULL,NULL);     
    fullscreen_ = Button_GetCheck(check_fullscreen_);
    mute_ = Button_GetCheck(check_mute_);
}


static BOOL OnCreateShortcut(HWND hwnd){
    static WCHAR desktop[MAX_PATH];
    static WCHAR exe[MAX_PATH];
    static WCHAR arg[1024];
    static WCHAR wname[MAX_PATH];
    int ret;
    if(!GetSpecialFolderPath(CSIDL_DESKTOP, desktop))return FALSE;
    GetModuleFileName(NULL, exe, MAX_PATH);    
    MultiByteToWideChar(CP_UTF8,0,video_device_,-1,wname,256);
    arg[0] = 0;
    wcsncat(arg, L"--video=\"", sizeof(arg));
    wcsncat(arg, wname, sizeof(arg));
    wcsncat(arg, L"\" ", sizeof(arg));
    MultiByteToWideChar(CP_UTF8,0,audio_device_,-1,wname,256);
    wcsncat(arg, L"--audio=\"", sizeof(arg));
    wcsncat(arg, wname, sizeof(arg));
    wcsncat(arg, L"\" ", sizeof(arg)); 
    if(mute_)wcsncat(arg, L"--mute ", sizeof(arg));  
    if(fullscreen_)wcsncat(arg, L"--fullscreen ", sizeof(arg));
    wcsncat(desktop, L"\\Capture Player.lnk", sizeof(desktop)); 
    ret = CreateShortcut(desktop, exe, arg, NULL, NULL);
    return ret;    
}

static INT_PTR WINAPI ConfigDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
    int ret;
    switch (msg){
    case WM_INITDIALOG:
        ret = InitDialog(hwnd);
        if(ret){
            EndDialog(hwnd, ret);
        }
        return TRUE;
    case WM_CLOSE:
        EndDialog(hwnd, -0xC0);
        return TRUE;
    case WM_COMMAND:
        switch (LOWORD(wParam)){
        case 1:  
            GetConfig();
            EndDialog(hwnd, 0);
            break;
        case 3001:
            GetConfig();
            OnCreateShortcut(hwnd);
            break;       
        default:
            return FALSE;
        }
        return TRUE;        
    default:
        break;
    }
    return FALSE;
}

static void ToggleMute(){
    mute_ = mute_?FALSE:TRUE;
    if(mute_){
        if(session_a_){
            media_session_stop(session_a_);
            media_session_destroy(session_a_);
            session_a_ = NULL;
        }
        if(audio_in_){
            audio_input_destory(audio_in_);
            audio_in_ = NULL;
        } 
        if(audio_out_){
            audio_output_destory(audio_out_);
            audio_out_ = NULL;
        }        
    }else{
        session_a_ = media_session_create();
        if(!session_a_)return;
        audio_in_ = audio_input_from_device(audio_device_);
        audio_out_ = audio_output_default();
        media_session_add_audio(session_a_, audio_in_, audio_out_);  
        media_session_start(session_a_);
    }
}


//窗口创建时
static BOOL MainWndInit(HWND hwnd, video_input_t vin){
    int ret;   
    LPCTSTR errmsg = NULL; 
    session_v_ = media_session_create();
    if(!session_v_){errmsg = TEXT("Create media session fail"); goto error;}    
    video_output_t vout = video_output_to_window((void*)hwnd);
    if(!vin){errmsg = TEXT("Create video output fail"); goto error;}
    ret = media_session_add_video(session_v_, vin, vout);  
    if(ret){errmsg = TEXT("Video midea session add video fail"); goto error;}
    ret = media_session_start(session_v_);
    if(ret){errmsg = TEXT("Video midea session start fail"); goto error;}
    if(fullscreen_){        
        FullScreen(hwnd, TRUE);
    }

    if(mute_){
        return TRUE;
    }
    session_a_ = media_session_create();
    if(!session_a_)return TRUE;
    audio_in_ = audio_input_from_device(audio_device_);
    audio_out_ = audio_output_default();
    media_session_add_audio(session_a_, audio_in_, audio_out_);    
    media_session_start(session_a_); 
    
    return TRUE;   
error:
    MessageBox(hwnd, errmsg, TEXT("CapturePlayer"), MB_OK);
    PostQuitMessage(1);
    return FALSE;
}

static void ToggleFullScreen(HWND hwnd){
    fullscreen_ = fullscreen_?FALSE:TRUE;
    FullScreen(hwnd, fullscreen_);    
}

static BOOL TakeSnapshot(){
    WCHAR imagedir[MAX_PATH]; 
    WCHAR savefile[MAX_PATH];
    char* aname;   
    SYSTEMTIME tm;  
    if(!GetSpecialFolderPath(CSIDL_MYPICTURES, imagedir))return FALSE;
    wcsncat(imagedir, L"\\CapturePlayer", MAX_PATH);    
    CreateDirectory(imagedir, NULL);
    GetLocalTime(&tm);
    snwprintf(savefile, MAX_PATH, L"%ls\\%04d%02d%02d-%02d%02d%02d.png", imagedir, 
        tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond);
    aname = (char* )imagedir;
    WideCharToMultiByte(CP_UTF8, 0, savefile, -1, aname, MAX_PATH, NULL, NULL);
    if(media_session_save_screenshot(session_v_, aname))return FALSE;
    return TRUE;
}

static BOOL OnKey(HWND hwnd, UINT code){
    switch (code){
    case VK_ESCAPE:
        if(fullscreen_){
            fullscreen_ = FALSE;
            FullScreen(hwnd, fullscreen_);
        }   
        return TRUE;
    case VK_F11:
        ToggleFullScreen(hwnd);
        return TRUE;   
    case VK_F9:
        ToggleMute();
        return TRUE;
    case '1':
    case VK_F8:
        TakeSnapshot();
        return TRUE;             
    default:
        break;
    }
    return FALSE;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
    switch (message){            
        case WM_DESTROY:
            PostQuitMessage(0);
            return 1;
        case WM_SIZE:            
            media_session_resize_video(session_v_, LOWORD(lParam), HIWORD(lParam));            
            return 1;
        case WM_KEYDOWN: 
            if(OnKey(hwnd, wParam))return 1; 
            break;    
        case WM_LBUTTONDBLCLK: 
            ToggleFullScreen(hwnd);
            return 1;              
        default:
            break;;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

BOOL RegisterWindowClass(){
    WNDCLASS wc = {0};
    wc.style = CS_VREDRAW|CS_HREDRAW|CS_DBLCLKS;    
    wc.hIcon = LoadIcon(hinstance_, MAKEINTRESOURCE(1));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpfnWndProc = MainWndProc;
    wc.hInstance = hinstance_;  
    wc.hbrBackground = GetStockBrush(BLACK_BRUSH);   
    wc.lpszClassName = TEXT("PlayerMainWindowClass");
    return RegisterClass(&wc);
}



static BOOL GetCommandString(LPTSTR lpCmdLine, LPCTSTR argname, char* buf, int buflen){
    WCHAR* varg = wcsstr(lpCmdLine, argname);
    WCHAR* vargend;
    int ret;
    if(!varg)return FALSE;
    varg += wcslen(argname);
    if(*varg == '\"'){
        varg ++;
        vargend = wcschr(varg, '\"');
        if(!vargend)return FALSE;
    }else{
        vargend = wcschr(varg, ' ');
        if(!vargend){
            vargend = lpCmdLine + wcslen(lpCmdLine);
        }
    }    
    ret = WideCharToMultiByte(CP_UTF8, 0, varg, vargend - varg, buf, buflen - 1, NULL, NULL);
    if(ret <= 0)return FALSE;
    buf[ret] = 0;    
    return TRUE;
}

static BOOL ParseCommand(LPTSTR lpCmdLine){
    BOOL ret;
    ret = GetCommandString(lpCmdLine, L"--video=", video_device_, sizeof(video_device_));
    if(!ret)return ret;
    ret = GetCommandString(lpCmdLine, L"--audio=", audio_device_, sizeof(audio_device_));
    if(!ret){
        mute_ = TRUE;
    }
    if(wcsstr(lpCmdLine, L"--mute"))mute_ = TRUE;
    if(wcsstr(lpCmdLine, L"--fullscreen"))fullscreen_ = TRUE;
    return TRUE;
}   


int WINAPI _tWinMain(HINSTANCE hInst,HINSTANCE hPrev,LPTSTR lpCmdLine,int nCmdShow){
    hinstance_ = hInst;    
    if(!ParseCommand(lpCmdLine)){
        int result = DialogBox(hInst, MAKEINTRESOURCE(101), NULL, ConfigDlgProc);
        if(result < 0){
            return -1;
        }
    }
    
    video_input_t vin = video_input_from_device(video_device_);
    int w = 800;
    int h = 600;
    video_input_get_size(vin, &w, &h);
    
    UINT dpi = GetDpiForSystem();
    if(dpi && dpi != 96){
        w = w * dpi / 96;
        h = h * dpi / 96;
    }
    RECT rc = {0,0,w,h};
    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
    RegisterWindowClass();
    HWND hwnd = CreateWindow(TEXT("PlayerMainWindowClass"),TEXT("CapturePlayer"),       
        WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT,CW_USEDEFAULT,rc.right-rc.left,rc.bottom-rc.top,NULL,NULL,hinstance_,NULL);    
    CenterWindow(hwnd);    
    ShowWindow(hwnd, nCmdShow);    
    MainWndInit(hwnd, vin);
    SetThreadExecutionState(ES_CONTINUOUS|ES_SYSTEM_REQUIRED|ES_DISPLAY_REQUIRED);
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0)){        
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
